package com.qire.common.widget.snackbar;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import java.lang.ref.WeakReference;

import static com.qire.common.widget.snackbar.BaseTransientBar.LifeStateChangeCallback.DISMISS_EVENT_CONSECUTIVE;
import static com.qire.common.widget.snackbar.BaseTransientBar.LifeStateChangeCallback.DISMISS_EVENT_TIMEOUT;

class SnackBarManager {
    static final int MSG_TIMEOUT = 0;
    private static final int SHORT_DURATION_MS = 1500;
    private static final int LONG_DURATION_MS = 2750;
    private static SnackBarManager snackbarManager;
    private final Object lock = new Object();
    private final Handler handler = new Handler(Looper.getMainLooper(), message -> {
        switch(message.what) {
            case MSG_TIMEOUT:
                handleTimeout((SnackBarRecord)message.obj);
                return true;
            default:
                return false;
        }
    });
    private SnackBarRecord currentSnackBar;
    private SnackBarRecord nextSnackBar;

    static SnackBarManager getInstance() {
        if (snackbarManager == null) {
            snackbarManager = new SnackBarManager();
        }

        return snackbarManager;
    }

    private SnackBarManager() {
    }

    public void show(int duration, Callback callback) {
        synchronized(this.lock) {
            if (this.isCurrentSnackBarLocked(callback)) {
                this.currentSnackBar.duration = duration;
                this.handler.removeCallbacksAndMessages(this.currentSnackBar);
                this.scheduleTimeoutLocked(this.currentSnackBar);
            } else {
                if (this.isNextSnackBarLocked(callback)) {
                    this.nextSnackBar.duration = duration;
                } else {
                    this.nextSnackBar = new SnackBarRecord(duration, callback);
                }

                if (this.currentSnackBar == null || !this.cancelSnackBarLocked(this.currentSnackBar, DISMISS_EVENT_CONSECUTIVE)) {
                    this.currentSnackBar = null;
                    this.showNextSnackBarLocked();
                }
            }
        }
    }

    public void dismiss(Callback callback, int event) {
        synchronized(this.lock) {
            if (this.isCurrentSnackBarLocked(callback)) {
                this.cancelSnackBarLocked(this.currentSnackBar, event);
            } else if (this.isNextSnackBarLocked(callback)) {
                this.cancelSnackBarLocked(this.nextSnackBar, event);
            }

        }
    }

    public void onDismissed(Callback callback) {
        synchronized(this.lock) {
            if (this.isCurrentSnackBarLocked(callback)) {
                this.currentSnackBar = null;
                if (this.nextSnackBar != null) {
                    this.showNextSnackBarLocked();
                }
            }

        }
    }

    public void onShown(Callback callback) {
        synchronized(this.lock) {
            if (this.isCurrentSnackBarLocked(callback)) {
                this.scheduleTimeoutLocked(this.currentSnackBar);
            }

        }
    }

    public void pauseTimeout(Callback callback) {
        synchronized(this.lock) {
            if (this.isCurrentSnackBarLocked(callback) && !this.currentSnackBar.paused) {
                this.currentSnackBar.paused = true;
                this.handler.removeCallbacksAndMessages(this.currentSnackBar);
            }

        }
    }

    public void restoreTimeoutIfPaused(Callback callback) {
        synchronized(this.lock) {
            if (this.isCurrentSnackBarLocked(callback) && this.currentSnackBar.paused) {
                this.currentSnackBar.paused = false;
                this.scheduleTimeoutLocked(this.currentSnackBar);
            }

        }
    }

    public boolean isCurrent(Callback callback) {
        synchronized(this.lock) {
            return this.isCurrentSnackBarLocked(callback);
        }
    }

    public boolean isCurrentOrNext(Callback callback) {
        synchronized(this.lock) {
            return this.isCurrentSnackBarLocked(callback) || this.isNextSnackBarLocked(callback);
        }
    }

    private void showNextSnackBarLocked() {
        if (this.nextSnackBar != null) {
            this.currentSnackBar = this.nextSnackBar;
            this.nextSnackBar = null;
            Callback callback = this.currentSnackBar.callback.get();
            if (callback != null) {
                callback.show();
            } else {
                this.currentSnackBar = null;
            }
        }

    }

    private boolean cancelSnackBarLocked(SnackBarRecord record, int event) {
        Callback callback = record.callback.get();
        if (callback != null) {
            this.handler.removeCallbacksAndMessages(record);
            callback.dismiss(event);
            return true;
        } else {
            return false;
        }
    }

    private boolean isCurrentSnackBarLocked(Callback callback) {
        return this.currentSnackBar != null && this.currentSnackBar.isSnackBar(callback);
    }

    private boolean isNextSnackBarLocked(Callback callback) {
        return this.nextSnackBar != null && this.nextSnackBar.isSnackBar(callback);
    }

    private void scheduleTimeoutLocked(SnackBarRecord r) {
        if (r.duration != -2) {
            int durationMs = LONG_DURATION_MS;
            if (r.duration > 0) {
                durationMs = r.duration;
            } else if (r.duration == -1) {
                durationMs = SHORT_DURATION_MS;
            }

            this.handler.removeCallbacksAndMessages(r);
            this.handler.sendMessageDelayed(Message.obtain(this.handler, MSG_TIMEOUT, r), durationMs);
        }
    }

    void handleTimeout(SnackBarRecord record) {
        synchronized(this.lock) {
            if (this.currentSnackBar == record || this.nextSnackBar == record) {
                this.cancelSnackBarLocked(record,DISMISS_EVENT_TIMEOUT);
            }

        }
    }

    private static class SnackBarRecord {
        final WeakReference<Callback> callback;
        int duration;
        boolean paused;

        SnackBarRecord(int duration, Callback callback) {
            this.callback = new WeakReference(callback);
            this.duration = duration;
        }

        boolean isSnackBar(Callback callback) {
            return callback != null && this.callback.get() == callback;
        }
    }

    interface Callback {
        void show();

        void dismiss(int var1);
    }
}
